package hanlen.mq.starter;

import hanlen.mq.starter.annotation.RedisQueueListener;
import hanlen.mq.starter.annotation.RedisSubscribe;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;

/**
 * @author weaponry MrYe
 * @program purchaser-assist-service
 * @description Redisson监听和Spring结合
 * @created 2020-09-24 20:39
 */
@Slf4j
public class RedissonBeanProcessor implements BeanPostProcessor, ApplicationListener<ContextClosedEvent>, ApplicationContextAware {

    private ApplicationContext context;

    private final List<RedisQueueBean> delayedBeans = new ArrayList<>();

    private final List<RedisSubscribeBean> redisBeans = new ArrayList<>();

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        final List<Method> queueMethods = getMethods(bean, RedisQueueListener.class);

        if (CollectionUtils.isNotEmpty(queueMethods) && context != null) {
            final RedisQueueBean<Object> listenerBean = new RedisQueueBean<>(bean, queueMethods);
            listenerBean.setApplicationContext(context);
            try {
                listenerBean.afterPropertiesSet();
            } catch (Exception e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            delayedBeans.add(listenerBean);
        }

        final List<Method> redisMethods = getMethods(bean, RedisSubscribe.class);

        if (CollectionUtils.isNotEmpty(redisMethods) && context != null) {
            final RedisSubscribeBean<Object> listenerBean = new RedisSubscribeBean<>(bean, redisMethods);
            listenerBean.setApplicationContext(context);
            try {
                listenerBean.afterPropertiesSet();
            } catch (Exception e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            redisBeans.add(listenerBean);
        }

        return bean;
    }

    private List<Method> getMethods(Object bean, Class<? extends Annotation> annotationClass) {
        return Stream.of(bean.getClass().getDeclaredMethods()).filter(m -> m.isAnnotationPresent(annotationClass)).collect(Collectors.toList());
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent closedEvent) {
        delayedBeans.forEach(RedisQueueBean::removeAllListener);
        redisBeans.forEach(RedisSubscribeBean::removeAllListener);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }

}
